package com.fingard.net.listener;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.util.Date;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;

import com.fingard.FGBiz;
import com.fingard.constant.Format;
import com.fingard.diagnostics.LogHelper;
import com.fingard.dsp.bank.directConfig.ListenerSet;
import com.fingard.dsp.bank.directbank.DirectBase;
import com.fingard.dsp.bank.directbank.bankInterface.IAcceptNotice;
import com.fingard.dsp.bank.reqrespobj.ReqRespHeader;
import com.fingard.generalobj.NoticeReq;
import com.fingard.generalobj.NoticeResp;
import com.fingard.io.FileHelper;


public class SocketThread implements Runnable {


    //private String bankCode;
    private SocketReceiver reqSocketReceiver;
    HashMap<String, ListenerSet> mapForBankClass = null;

    public SocketThread(SocketReceiver p_SocketReceiver, HashMap<String, ListenerSet> p_map) {
        reqSocketReceiver = p_SocketReceiver;
        mapForBankClass = p_map;
    }

    public void run() {
        try {
            doRequest();
        } catch (Exception ex) {
            ex.printStackTrace();

        }

    }

    private static AtomicInteger concurrentNum = new AtomicInteger();
    private static int maxConcurrent = 0;
    private static Date maxHappenedTime = new Date();
    
    private String bankCode = "";

    private void doRequest() {

        int tmpConcurrentNum = concurrentNum.incrementAndGet();
        if (tmpConcurrentNum > maxConcurrent) {
            maxConcurrent = tmpConcurrentNum;
            maxHappenedTime = new Date();
        }

        DataOutputStream tmpRespOutput = null;
        DataInputStream tmpReqInput = null;

        StringBuilder sbLog = new StringBuilder();
        sbLog.append(Format.DateTimeSSSFormat.format(reqSocketReceiver.connTime) + " 开始接收\r\n");
        String visitPage = "/";
        bankCode = "null";
        try {
        	String duplexing = "";
            //String tmpRespHead = "";
        	NoticeResp ntResp = new NoticeResp();
            String reqCharset = reqSocketReceiver.initCharset;
            if (mapForBankClass != null && mapForBankClass.containsKey(visitPage)) {
                bankCode = mapForBankClass.get(visitPage).bankFront.bankCode;
                String className = "com.fingard.dsp.bank.directbank." + bankCode.toLowerCase() + "." + bankCode + "AcceptNotice";
                Class<?> bankClass = Class.forName(className);
                if (bankClass != null) {
                    DirectBase tmpDirectBase = (DirectBase) bankClass.newInstance();
                    tmpDirectBase.reqHeader = new ReqRespHeader();
                    tmpDirectBase.reqHeader.bankCode = bankCode;
                    tmpDirectBase.bankFront = mapForBankClass.get(visitPage).bankFront;
                    tmpDirectBase.setSbLog(sbLog);
                    IAcceptNotice iNotice = (IAcceptNotice) tmpDirectBase;
                    int ttSec = mapForBankClass.get(visitPage).timeoutSec;
                    if(ttSec > 0){
                    	sbLog.append("超时时间："+ttSec+"秒\r\n");
                    	reqSocketReceiver.socketClient.setSoTimeout(ttSec*1000);
                    }
                    boolean keepAlive = mapForBankClass.get(visitPage).keepAlive;
                    if(keepAlive){
                    	sbLog.append("keepAlive：true\r\n");
                    	reqSocketReceiver.socketClient.setKeepAlive(true);
                    }
                    
                    duplexing = mapForBankClass.get(visitPage).duplexing;
                    
                    int initHeadByteCount = 2048;//2K
                    if(keepAlive && mapForBankClass.get(visitPage).initHeadByteCount > 0){
                    	initHeadByteCount = mapForBankClass.get(visitPage).initHeadByteCount;
                    }

                    tmpReqInput = new DataInputStream(reqSocketReceiver.socketClient.getInputStream());
                    
                    reqSocketReceiver.reqBytes = new byte[initHeadByteCount];
                    int tmpOffSet = 0;
                    
                    while(true){
                        while (!reqSocketReceiver.isCompleted) {
                        	int tmpReadLen = 0;
                        	if(keepAlive){
                        		//java.net.SocketTimeoutException: Read timed out
                        		try{
                        			tmpReadLen = tmpReqInput.read(reqSocketReceiver.reqBytes, tmpOffSet, reqSocketReceiver.reqBytes.length - tmpOffSet);
                        		}catch(Exception ex){
                        			String exMsg = LogHelper.getStackTraceFirstLine(ex);
                        			if(exMsg.contains("SocketTimeoutException") || exMsg.contains("Read timed out")){
                        				if (reqSocketReceiver.readedLength > 0) {
                        					reqSocketReceiver.reset();
                                        	reqSocketReceiver.reqBytes = new byte[initHeadByteCount];
                                            tmpOffSet = 0;
                        				}
                                    	continue;
                        			}else{
                        				if(tmpRespOutput!=null){
                        					tmpRespOutput.close();
                        				}
                        				throw(ex);
                        			}
                                	
                        		}
                        	}else{
                        		tmpReadLen = tmpReqInput.read(reqSocketReceiver.reqBytes, tmpOffSet, reqSocketReceiver.reqBytes.length - tmpOffSet);
                        	}
                            sbLog.append(Format.DateTimeSSSFormat.format(reqSocketReceiver.connTime) + " 读取字节数：" + tmpReadLen + "\r\n");
                            if (tmpReadLen <= 0) {
                                break;
                            }
                            tmpOffSet += tmpReadLen;
                            reqSocketReceiver.readedLength = reqSocketReceiver.readedLength + tmpReadLen;
                            if (reqSocketReceiver.contentLength == -1) {
                                iNotice.getContentLength(reqSocketReceiver);
                            }

                            if (reqSocketReceiver.contentLength > 0) {
                                if (reqSocketReceiver.reqBytes.length < reqSocketReceiver.contentLength + reqSocketReceiver.contentStartIndex) {
                                    byte[] tmpUnionInfo = new byte[reqSocketReceiver.contentLength + reqSocketReceiver.contentStartIndex];//一次性分配内存
                                    System.arraycopy(reqSocketReceiver.reqBytes, 0, tmpUnionInfo, 0, tmpOffSet);
                                    reqSocketReceiver.reqBytes = null;
                                    reqSocketReceiver.reqBytes = tmpUnionInfo;
                                }

                                int tmpToReadLen = reqSocketReceiver.contentLength + reqSocketReceiver.contentStartIndex - tmpOffSet;
                                if (tmpToReadLen <= 0) {
                                    reqSocketReceiver.isCompleted = true;
                                    break;
                                }
                            } else if (reqSocketReceiver.reqBytes.length - tmpOffSet < 1024) {//扩展
                                byte[] tmpUnionInfo2 = new byte[tmpOffSet + 20480];
                                System.arraycopy(reqSocketReceiver.reqBytes, 0, tmpUnionInfo2, 0, tmpOffSet);
                                reqSocketReceiver.reqBytes = null;
                                reqSocketReceiver.reqBytes = tmpUnionInfo2;
                            }
                        }
                        if(keepAlive){
                        	if (reqSocketReceiver.readedLength == 0) {
                        		if("half".equals(duplexing)){
                        			break;
                        		}else{
                        			continue;
                        		}
                        	}else{
                        		reqSocketReceiver.recCompleteTime = new Date();
                        	}
                        }else{
                        	reqSocketReceiver.recCompleteTime = new Date();

                            if (reqSocketReceiver.readedLength == 0) {
                                //F5等设备的监测
                                tmpReqInput.close();
                                tmpReqInput = null;
                                reqSocketReceiver.socketClient.close();
                                return;
                            }
                        }
                        
                        
                        sbLog.append(Format.DateTimeSSSFormat.format(reqSocketReceiver.recCompleteTime) + " 接收到请求字节数："+reqSocketReceiver.readedLength+"\r\n");
                        
                        NoticeReq req = new NoticeReq();
                        req.reqCharset = reqCharset;
                        req.contentStartIndex = reqSocketReceiver.contentStartIndex;
                        req.contentLength = reqSocketReceiver.contentLength;
                        req.reqBytes = new byte[reqSocketReceiver.readedLength];
                        System.arraycopy(reqSocketReceiver.reqBytes, 0, req.reqBytes, 0, reqSocketReceiver.readedLength);
                        ntResp = iNotice.dealNotice(req);
                        if(keepAlive){
                        	writeBankLogs(sbLog);
                        	if("half".equals(duplexing)){
                        		if(tmpRespOutput==null){
                                	tmpRespOutput = new DataOutputStream(reqSocketReceiver.socketClient.getOutputStream());
                                }
                        		
                                tmpRespOutput.write(ntResp.respBytes);
                                if(ntResp.closeConn){
                                	break;
                                }
                        	}
                        	sbLog = new StringBuilder();
                        	tmpDirectBase.setSbLog(sbLog);
                        	reqSocketReceiver.reset();
                        	reqSocketReceiver.reqBytes = new byte[initHeadByteCount];
                            tmpOffSet = 0;
                        	continue;
                        }else{
                        	break;
                        }
                    }
                }
            }
            
            if(ntResp.respBytes == null){
            	ntResp.respBytes = "Bad Request!".getBytes(reqCharset);
            }

            if(!"half".equals(duplexing) && ntResp.respBytes!=null){
	            if(tmpRespOutput==null){
	            	tmpRespOutput = new DataOutputStream(reqSocketReceiver.socketClient.getOutputStream());
	            }
	
	            //byte[] tmpBytesHead = tmpRespHead.getBytes(reqCharset);
	            //byte[] tmpBytesBody = tmpRespHead.getBytes(reqCharset);
	            //byte[] tmpBytesResp = new byte[tmpBytesHead.length + tmpBytesBody.length];
	            //System.arraycopy(tmpBytesHead, 0, tmpBytesResp, 0, tmpBytesHead.length);
	            //System.arraycopy(tmpBytesBody, 0, tmpBytesResp, tmpBytesHead.length, tmpBytesBody.length);
	
	            //sbLog.append(Format.DateTimeSSSFormat.format(new Date()) + " 响应：" + tmpRespHead + "\r\n");
	
	            tmpRespOutput.write(ntResp.respBytes);
            }

            tmpReqInput.close();
            tmpReqInput = null;
            if(tmpRespOutput != null){
	            tmpRespOutput.close();
	            tmpRespOutput = null;
            }
            //System.out.println("处理完"+p_receiver.seqIDInner);


        } catch (Throwable e) {
        	FGBiz.limitMsg.exception("net.listener."+bankCode, e);
            sbLog.append(LogHelper.getStackTrace(e) + "\r\n");
        } finally {

            try {

                if (tmpReqInput != null) {
                    tmpReqInput.close();
                }
                if (tmpRespOutput != null) {
                    tmpRespOutput.close();
                }
                reqSocketReceiver.socketClient.close();
                tmpConcurrentNum = concurrentNum.decrementAndGet();
                if (tmpConcurrentNum == 0) {
                    Date tmpTimeNow = new Date();
                    long tmpMilSeconds = tmpTimeNow.getTime() - maxHappenedTime.getTime();
                    long tmpSeconds = tmpMilSeconds / 1000;
                    if (tmpSeconds >= 60) {
                        maxConcurrent = 0;
                    }
                }

            } catch (Exception e) {
            	FGBiz.limitMsg.exception("net.listener."+bankCode, e);
                sbLog.append(LogHelper.getStackTrace(e) + "\r\n");
            }
        }

        writeBankLogs(sbLog);

    }
    
    private void writeBankLogs(StringBuilder sbLog){
    	try {
            Date finishTime = new Date();
            //String tmpStrLogSeq = String.format("%04d", reqSocketReceiver.seqIDInner);
            String tmpDateDir = Format.DateForLogFormat.format(finishTime).replace("-", File.separator);

            String tmpSubFileName = "ListenerLogs_" + reqSocketReceiver.ip + File.separator + tmpDateDir + File.separator + bankCode + "_" + reqSocketReceiver.port + File.separator + bankCode + "_" + Format.DateForLogFormat.format(finishTime) + ".txt";

            String tmpLogFile = FGBiz.dspCenter.bankLogsDirPath + File.separator + tmpSubFileName;
            sbLog.append("\r\n");

            FileHelper.appendFile(tmpLogFile, sbLog.toString(), FGBiz.dspCenter.charset);
        } catch (Exception e) {
            FGBiz.limitMsg.exception("listener.SocketThread.writeBankLogs", e);
        }
    }
    
}
